#include "../../public/_public.h"

using namespace idc;

ctcpserver tcpserver;
clogfile logfile;
cpactive pactive;

string strsendmessage, strrecvmessage;

void FathExit(int sig);
void ChldExit(int sig);

void recvfiles();                      // 接收文件
bool activetest();                     // 心跳报文
bool clientlogin();                    // 处理客户端登录
bool sendfile(const string &filename,const int filesize);                       // 把文件的内容发送给对端。
void sendfiles();                      // 发送文件
bool _tcpputfile(bool& bcontinue);  //  文件下载的主函数，执行一次文件下载的任务
bool ackmessage(const string &strrecvbuffer); // 处理客户端的应答报文
bool recvfile(const string& filename,const int filesize,const string& mtime); // 接收文件

struct st_arg
{
    int clienttype;          // 客户端的类型  1-上传 2-下载
    char ip[32];             // 服务端的ip地址
    int port;                // 服务端的端口
    char clientpath[256];    // 客户端的路径
    int ptype;               // 操作后=本地文件的处理方式  1-删除 2-备份
    char serverpathbak[256]; // 备份的路径
    bool andchild;           // 是否上传子目录的文件
    char matchname[256];     // 文件名匹配的规则
    char serverpath[256];    // 服务端的路径
    int timeval;             // 执行文件上传工作的时间间隔
    int timeout;             // 心跳超时时间
    char pname[256];         // 进程名
} starg;

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("Using:./fileserver port logfile\n");
    }
    closeallsign();
    signal(2, FathExit);
    signal(15, FathExit);
    if (logfile.open(argv[2]) == false)
    {
        printf("logfile open failed.\n");
    }
    if (tcpserver.Initserver(atoi(argv[1])) == false)
    {
        logfile.write("tcpserver initserver failed.\n");
        return -1;
    }
    while (true)
    {
        if (tcpserver.Accept() == false)
        {
            logfile.write("tcpserver accept failed.\n");
            FathExit(-1);
        }
        if (fork() > 0) // 此时为父进程，只需要继续循环等待新的连接，不需要监听客户端
        {
            tcpserver.Closeconn();
            continue;
        }
        tcpserver.Closelisten(); // 此时为子进程，关闭监听，开始处理客户端请求
        signal(2, ChldExit);
        signal(15, ChldExit);
        if (clientlogin() == false)
        {
            logfile.write("clientlogin failed.\n");
            ChldExit(-1);
        }
        pactive.init(starg.timeout,starg.pname);
        if (starg.clienttype == 1)
        {
            recvfiles();
        }
        else if (starg.clienttype == 2)
        {
            sendfiles();
        }
        ChldExit(0);
    }
    return 0;
}

void FathExit(int signo)
{
    signal(2, SIG_IGN);
    signal(15, SIG_IGN);
    logfile.write("father exit,sign=%d\n", signo);
    tcpserver.Closelisten();
    exit(0);
}

void ChldExit(int signo)
{
    signal(2, SIG_IGN);
    signal(15, SIG_IGN);
    logfile.write("child exit,sign=%d\n", signo);
    tcpserver.Closelisten();
    exit(0);
}

void recvfiles()
{
    while(true)
    {
        pactive.update();
        if(tcpserver.Read(strrecvmessage,20)==false)
        {
            logfile.write("recvfiles read failed.\n");
            return;
        }
        if(strrecvmessage=="<activetest>ok</activetest>")  //心跳报文
        {
            strsendmessage = "ok";
            if(tcpserver.Write(strsendmessage)==false)
            {
                logfile.write("recvfiles write failed.\n");
                return;
            }
        }
        if(strrecvmessage.find("<filename>")!=string::npos)
        {
            string filename,mtime;
            int size=0;
            getxmlbuffer(strrecvmessage,"filename",filename,256);
            getxmlbuffer(strrecvmessage,"size",size);
            getxmlbuffer(strrecvmessage,"mtime",mtime,256);
            string serverfilename=filename;
            string clientpath=starg.clientpath;
            string serverpath=starg.serverpath;
            replacestr(serverfilename,clientpath,serverpath,false);
            if (recvfile(serverfilename,size,mtime)==true)
            {
                logfile << "ok.\n";
                sformat(strsendmessage,"<filename>%s</filename><result>ok</result>",filename.c_str());
            }
            else
            {
                logfile << "failed.\n";
                sformat(strsendmessage,"<filename>%s</filename><result>failed</result>",filename.c_str());
            }
            if(tcpserver.Write(strsendmessage)==false)
            {
                logfile.write("recvfiles write failed.\n");
                return;
            }
        }
    }
}


bool recvfile(const string& filename,const int filesize,const string& mtime)
{
    int totalread = 0;
    char buffer[1024];
    int oneread=1024;
    cofile ofile;
    if(ofile.open(filename.c_str(),true,ios::out|ios::binary)==false)
    { 
        logfile.write("open file %s failed.\n",filename.c_str());
        return false;
    }
    while(true)
    {
        memset(buffer,0,sizeof(buffer));
        if(filesize-totalread<oneread)
        {
            oneread= filesize-totalread;
        }
        if(tcpserver.Read(buffer,oneread)==false)
        {
            logfile.write("recvfile read failed.\n");
            return false;
        }
        ofile.write(buffer,oneread);
        totalread+=oneread;
        if(totalread==filesize) break;
    }
    ofile.closeandrename();
    idc::setmtime(filename,mtime);
    return true;
}

void sendfiles()
{
    pactive.init(starg.timeout,starg.pname);
    bool bcontinue=true;
    while(true)
    {
        pactive.update();
        if(_tcpputfile(bcontinue)==false)
        {
            logfile.write("sendfiles failed.\n");
        }
        if(bcontinue==false)
        {
            sleep(starg.timeval);
            activetest();
        }
    }
}

bool _tcpputfile(bool& bcontinue)
{
        bcontinue=false;
    cdir dir;
    if(dir.opendir(starg.clientpath,starg.matchname,10000,starg.andchild)==false)
    {
        logfile.write("dir.opendir(%s) failed.\n",starg.clientpath);
        return false;
    }
    int delay=0; //用来计数,每成功上传一个文件就加1,接收一次确认报文减一
    while(dir.readdir())
    {
        bcontinue=true;
        sformat(strsendmessage,"<filename>%s</filename><mtime>%s</mtime><size>%d</size>",dir.m_ffilename.c_str(),dir.m_mtime.c_str(),dir.m_filesize);
        if(tcpserver.Write(strsendmessage)==false)
        {
            logfile.write("tcpclient.Write(%s) failed.\n",strsendmessage.c_str());
            return false;
        }
        // 发送文件内容。
        logfile.write("send %s(%d) ...",dir.m_ffilename.c_str(),dir.m_filesize);
        if (sendfile(dir.m_ffilename,dir.m_filesize)==true)
        {
            logfile << "ok.\n"; 
            delay++;
        }
        else
        {
            logfile << "failed.\n"; tcpserver.Closeconn(); return false;
        }
        while(delay>0)
        {
            if(tcpserver.Read(strrecvmessage,-1)==false)
            {
                logfile.write("tcpclient.Read() failed.\n");
                return false;
            }
            if(ackmessage(strrecvmessage)==false)
            {
                logfile.write("askmessage() failed.\n");
                return false;
            }
            delay--;
        }
    }
    while(delay>0)
    {
        if(tcpserver.Read(strrecvmessage,-1)==false)
        {
            logfile.write("tcpclient.Read() failed.\n");
            return false;
        }
        if(ackmessage(strrecvmessage)==false)
        {
            logfile.write("askmessage() failed.\n");
        }
        delay--;
    }
    return true;
}

bool sendfile(const string &filename,const int filesize)
{
    int oneread=1024;  //每次读取文件大小
    char buffer[1024]; //存放读取文件内容
    int readcount=0;   //读取文件计数
    cifile ifile;
    if( ifile.open(filename.c_str(),ios::in|ios::binary)==false)
    {
        logfile.write("open(%s) failed.\n",filename.c_str());
        return false;
    }
    while(1)
    {
        memset(buffer,0,sizeof(buffer));
        if(filesize-readcount<oneread)
        {
            oneread=filesize-readcount;
        }
        if(ifile.read(buffer,oneread)==false)
        {
            logfile.write("ifile.read() failed.\n");
            return false;
        }
        if(tcpserver.Write(buffer,oneread)==false)
        {
            logfile.write("tcpclient.Write() failed.\n");
            return false;
        }
        readcount+=oneread;
        if(readcount==filesize)  break;
    }
    return true;
}

bool ackmessage(const string& strrecvmessage)
{
    string result,filename;
    getxmlbuffer(strrecvmessage,"result",result);
    getxmlbuffer(strrecvmessage,"filename",filename);
    if(result=="ok")
    {
        logfile.write("filename=%s\n put ok\n",filename.c_str());
    }
    else
    {
        logfile.write("filename=%s\n put failed\n",filename.c_str());
    }
    if(starg.ptype==1)
    {
        if(remove(filename.c_str())==false)
        {
            perror("remove");
            logfile.write("remove(%s) failed.\n",filename.c_str());
            return false;
        }
    }
    else
    {
        string tmpfilename;
        tmpfilename=replacestr(filename,starg.serverpath,starg.serverpathbak);
        if(rename(filename.c_str(),tmpfilename.c_str())==false)
        {
            logfile.write("rename(%s,%s) failed.\n",filename.c_str(),tmpfilename.c_str());
            return false;
        }
    }
    return true;
}


bool clientlogin()
{
    memset(&starg, 0, sizeof(starg));
    if (tcpserver.Read(strrecvmessage, 20) == false)
    {
        logfile.write("clientlogin read failed.\n");
        return false;
    }
    getxmlbuffer(strrecvmessage, "clienttype", starg.clienttype);
    getxmlbuffer(strrecvmessage, "clientpath", starg.clientpath, 256);
    getxmlbuffer(strrecvmessage, "serverpath", starg.serverpath, 256);
    getxmlbuffer(strrecvmessage, "serverpathbak", starg.serverpathbak, 256);
    getxmlbuffer(strrecvmessage,"andchild",starg.andchild);
    getxmlbuffer(strrecvmessage,"ptype",starg.ptype);
    getxmlbuffer(strrecvmessage,"matchname",starg.matchname);

    getxmlbuffer(strrecvmessage,"timetvl",starg.timeval);        // 执行任务的周期。
    getxmlbuffer(strrecvmessage,"timeout",starg.timeout);     // 进程超时的时间。
    getxmlbuffer(strrecvmessage,"pname",starg.pname,50);    // 进程名。
    if (starg.clienttype != 1 && starg.clienttype != 2)
    {
        logfile.write("clienttype=%d is invalid.\n", starg.clienttype);
        strsendmessage = "<error>valid</error>";
    }
    else
    {
        strsendmessage = "<login>ok<login>";
    }
    if (tcpserver.Write(strsendmessage) == false)
    {
        logfile.write("clientlogin write failed.\n");
    }
    logfile.write("%s login success.\n", tcpserver.getclientip());
    return true;
}

bool activetest()
{
    strsendmessage="<activetest>ok</activetest>";
    // xxxxxxxxxxxxx logfile.write("发送：%s\n",strsendbuffer.c_str());
    if (tcpserver.Write(strsendmessage)==false) return false; // 向服务端发送请求报文。

    if (tcpserver.Read(strrecvmessage,20)==false) return false; // 接收服务端的回应报文。
    // xxxxxxxxxxxxx logfile.write("接收：%s\n",strrecvbuffer.c_str());

    return true;
}
